﻿using System.Collections.Concurrent;

using GCCGrpc.FileServer;

using Grpc.Core;
using Grpc.Net.Client;

using static GCCGrpc.FileServer.FileServerGrpc;

namespace System.FileServer.Client
{
    /// <summary>
    /// 文件服务器客户端管理器
    /// </summary>
    public class Manager
    {
        private static readonly ConcurrentDictionary<string, GrpcChannel> _allChannels = new();

        /// <summary>
        /// 获取所有的文件列表
        /// </summary>
        /// <param name="host">文件服务器主机地址</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">每页大小</param>
        /// <param name="currentEventID">当前事件ID</param>
        /// <returns></returns>
        public static GRPCGetAllItemsResponse? GRPCGetAllItems(string host, int pageIndex, int pageSize, string currentEventID)
        {
            var metaData = new Metadata();
            if (!string.IsNullOrEmpty(currentEventID))
            {
                metaData.Add("GCCEventID", currentEventID);
            }
            var request = new GRPCGetAllItemsRequest()
            {
                PageIndex = pageIndex,
                PageSize = pageSize
            };
            var channel = _allChannels.GetOrAdd(host, item => GrpcChannel.ForAddress(host));
            var response = new FileServerGrpcClient(channel)
                .GRPCGetAllItems(request, metaData);
            return response;
        }

        /// <summary>
        /// 根据文件的校验码获取文件
        /// </summary>
        /// <param name="host">文件服务器主机地址</param>
        /// <param name="checkCode">校验码</param>
        /// <param name="currentEventID">当前事件ID</param>
        /// <returns></returns>
        public static GRPCGetFilesByCheckCodeResponse? GRPCGetFilesByCheckCode(string host, string checkCode, string currentEventID)
        {
            var metaData = new Metadata();
            if (!string.IsNullOrEmpty(currentEventID))
            {
                metaData.Add("GCCEventID", currentEventID);
            }
            var request = new GRPCGetFilesByCheckCodeRequest()
            {
                CheckCode = checkCode
            };
            var channel = _allChannels.GetOrAdd(host, item => GrpcChannel.ForAddress(host));
            var response = new FileServerGrpcClient(channel)
                .GRPCGetFilesByCheckCode(request, metaData);
            return response;
        }

        /// <summary>
        /// 根据ID查找一个文件
        /// </summary>
        /// <param name="host">文件服务器主机地址</param>
        /// <param name="fid">文件ID</param>
        /// <param name="currentEventID">当前事件ID</param>
        /// <returns></returns>
        public static GRPCGetFileByIDResponse? GRPCGetFileByID(string host, long fid, string currentEventID)
        {
            var metaData = new Metadata();
            if (!string.IsNullOrEmpty(currentEventID))
            {
                metaData.Add("GCCEventID", currentEventID);
            }
            var request = new GRPCGetFileByIDRequest()
            {
                FID = fid
            };
            var channel = _allChannels.GetOrAdd(host, item => GrpcChannel.ForAddress(host));
            var response = new FileServerGrpcClient(channel)
                .GRPCGetFileByID(request, metaData);
            return response;
        }

        /// <summary>
        /// 创建一个文件
        /// </summary>
        /// <param name="host">文件服务器主机地址</param>
        /// <param name="files">要上传的文件地址</param>
        /// <param name="mimeMapping">文件Http类型</param>
        /// <param name="currentEventID">当前事件ID</param>
        /// <returns></returns>
        public static GRPCCreateFilesResponse? GRPCCreateFiles(string host, List<FileInfo> files
            , string currentEventID
            , Dictionary<string, string>? mimeMapping = null
            )
        {
            var metaData = new Metadata();
            if (!string.IsNullOrEmpty(currentEventID))
            {
                metaData.Add("GCCEventID", currentEventID);
            }
            var request = new GRPCCreateFilesRequest();
            files.ForEach(fileItem =>
            {
                using var fs = fileItem.OpenRead();
                var allBytes = new byte[fs.Length];
                _ = fs.Read(allBytes);
                var item = new GRPCCreateFilesRequestItem();
                if (mimeMapping is not null && mimeMapping.TryGetValue(fileItem.Name, out var mime))
                {
                    item.ContentType = mime;
                }
                else
                {
                    item.ContentType = "application/octet-stream";
                }
                item.Content = Google.Protobuf.ByteString.CopyFrom(allBytes);
                item.FileName = fileItem.Name;
                request.Items.Add(item);
            });
            var channel = _allChannels.GetOrAdd(host, item => GrpcChannel.ForAddress(host));
            var response = new FileServerGrpcClient(channel)
                .GRPCCreateFiles(request, metaData);
            return response;
        }

        /// <summary>
        /// 分片上传大文件
        /// </summary>
        /// <param name="host">文件服务器主机地址</param>
        /// <param name="filePath">要上传的文件地址</param>
        /// <param name="mimeType">文件Http类型</param>
        /// <param name="currentEventID">当前事件ID</param>
        /// <returns></returns>
        public static IEnumerable<(GRPCCreateBigFileResponse res, long index, long totalPackage, string token)> GRPCCreateBigFile(
            string host
            , string filePath
            , string currentEventID
            , string mimeType = "application/octet-stream")
        {
            var metaData = new Metadata();
            if (!string.IsNullOrEmpty(currentEventID))
            {
                metaData.Add("GCCEventID", currentEventID);
            }
            var channel = _allChannels.GetOrAdd(host, item => GrpcChannel.ForAddress(host));


            using var fs = File.OpenRead(filePath);
            var fi = new FileInfo(filePath);
            var buffer = new byte[1024 * 1024 * 20];
            var currentOffset = 0;
            var totalPackages = (fs.Length / buffer.Length) + ((fs.Length % buffer.Length) == 0 ? 0 : 1);
            var index = 0L;
            var token = Guid.NewGuid().ToString("N");
            while (fs.Length != currentOffset)
            {
                var thisLoop = fs.Read(buffer);
                var request = new GRPCCreateBigFileRequest()
                {
                    Content = Google.Protobuf.ByteString.CopyFrom(buffer[0..thisLoop]),
                    ContentType = mimeType,
                    FileName = fi.Name,
                    FileSize = fs.Length,
                    OffSet = currentOffset,
                    PackageCount = totalPackages,
                    ThisPackageIndex = index,
                    Token = token
                };
                var res = new FileServerGrpcClient(channel)
                .GRPCCreateBigFile(request, metaData);
                currentOffset += thisLoop;
                index++;
                yield return (res, index, totalPackage: totalPackages, token);
            }
        }
    }
}
